#include <all_header.h>

#define ABORT()		ASSERT(0);

struct {
	uint8_t flag;
	uint8_t reserved[100];
} __attribute__ ((aligned (16))) flash_data;

#define FLASH_MAP_ADDR				0x08000000
#define DATA_FLASH_SIZE				sizeof(flash_data)
#define DATA_FLASH_START			(120*1024)
#undef FLASH_BANK_SIZE  /// 有时读取时会报错
#define FLASH_BANK_SIZE				(1024*1024)

static SemaphoreHandle_t sem;
static SemaphoreHandle_t mutex;
static EventGroupHandle_t event;

static int get_bank(uint32_t start) {
	if (start >= FLASH_BANK_SIZE)
		return FLASH_BANK_2;
	return FLASH_BANK_1;
}

static int get_sector(uint32_t start) {
	if (start >= FLASH_BANK_SIZE) {
		return (start - FLASH_BANK_SIZE) / FLASH_SECTOR_SIZE;
	}
	return start / FLASH_SECTOR_SIZE;
}

static void erase_data() {
	uint32_t SectorError;
	FLASH_EraseInitTypeDef EraseInitStruct;
	MEM_ZERO(EraseInitStruct);

	EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
	EraseInitStruct.Banks = get_bank(DATA_FLASH_START);
	EraseInitStruct.Sector = get_sector(DATA_FLASH_START);
	EraseInitStruct.NbSectors = 1;

	if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
		ABORT();
	}
}

static void get_data() {
	memcpy(&flash_data, (void*) (FLASH_MAP_ADDR + DATA_FLASH_START),
	DATA_FLASH_SIZE);
}

static void save_data() {
	int size = 0;
	while (size < DATA_FLASH_SIZE) {
		if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD,
		FLASH_MAP_ADDR + DATA_FLASH_START + size, (uint32_t) &flash_data + size)
				!= HAL_OK) {
			ABORT();
		}
		size += 16;
	}
}

void flash_data_task_init(SemaphoreHandle_t mtx) {
	sem = xSemaphoreCreateBinary();
	ASSERT(sem);

	event = xEventGroupCreate();
	ASSERT(event);

	mutex = mtx;
	get_data();
}

void flash_data_task_run(void *arg) {
	while (true) {
		ASSERT(xSemaphoreTake(sem,portMAX_DELAY) == pdTRUE);

		ASSERT(xSemaphoreTake(mutex,portMAX_DELAY) == pdTRUE);
		HAL_ICACHE_Disable();
		ASSERT(HAL_FLASH_Unlock() == HAL_OK);
		erase_data();
		save_data();
		get_data();
		ASSERT(HAL_FLASH_Lock() == HAL_OK);
		HAL_ICACHE_Enable();
		xEventGroupSetBits(event, 1);
		xEventGroupClearBits(event, 1);
		ASSERT(xSemaphoreGive(mutex) == pdTRUE);
	}
}

static inline void notify(void) {
	xSemaphoreGive(sem);
}

uint8_t flash_data_task_get_flag(void) {
	return flash_data.flag;
}

void flash_data_task_set_flag(uint8_t flag) {
	if (flash_data.flag != flag) {
		flash_data.flag = flag;
		notify();
		xEventGroupWaitBits(event, 1, pdTRUE, pdTRUE, portMAX_DELAY);
	}
}
